package com.yuantong.xyerp.rest.config.interceptors;


import com.alibaba.fastjson.JSON;
import com.yuantong.xyerp.core.api.common.Constant;
import com.yuantong.xyerp.core.api.enums.EnumMsgCode;
import com.yuantong.xyerp.core.api.exception.YuantongException;
import com.yuantong.xyerp.core.api.model.Menu;
import com.yuantong.xyerp.core.api.model.UserDetail;
import com.yuantong.xyerp.core.api.model.base.RestResponseModel;
import com.yuantong.xyerp.core.api.service.IUserService;
import com.yuantong.xyerp.rest.config.shiro.jwt.JwtIgnore;
import com.yuantong.xyerp.rest.config.shiro.jwt.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


@Slf4j
public class JwtInterceptor implements HandlerInterceptor {

    // @Resource
    // private UserMapper userMapper;
    @Autowired
    private IUserService userService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws YuantongException {

        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            JwtIgnore jwtIgnore = handlerMethod.getMethodAnnotation(JwtIgnore.class);
            if (jwtIgnore != null) {
                return true;
            }
            final String authHeader = request.getHeader(JwtUtil.AUTH_HEADER_KEY);
            log.info("(每次)Authorization {}", authHeader);
            // 判断有无登录认证
            if (StringUtils.isBlank(authHeader) || !authHeader.startsWith(JwtUtil.TOKEN_PREFIX)) {
                String httpMethod = request.getMethod();
                String requestURI = request.getRequestURI();
                log.info("当前请求 {} Authorization属性(Token)为空 请求类型 {}", requestURI, httpMethod);
                response401(response,EnumMsgCode.USER_NOT_LOGGED_IN,null);
                return false;
            }
            // 获取token
            String token = authHeader.substring(7); //去除 “Bearer "
            // 黑名单
            if (Boolean.TRUE.equals(redisTemplate.hasKey(Constant.PREFIX_FORBID_TOKEN_CACHE + token))){
                response401(response,EnumMsgCode.USER_NOT_LOGGED_IN,null);
                return false;
            }
            String loginName = JwtUtil.getClaim(token, Constant.LOGIN_NAME);
            // if这一步主要是 为了更新缓存
            UserDetail userDetail;
            if (!Boolean.TRUE.equals(redisTemplate.hasKey(Constant.PREFIX_USER_CACHE + loginName))){
                userDetail = userService.selectOneByUserDetail(new UserDetail().setLoginName(loginName));
                redisTemplate.opsForValue()
                        .set(Constant.PREFIX_USER_CACHE + loginName, userDetail, Constant.USER_EXPIRE_TIME, TimeUnit.SECONDS);
            }else
                userDetail = (UserDetail) redisTemplate.opsForValue().get(Constant.PREFIX_USER_CACHE + loginName);
            // todo: 服务端修改了某个用户具有的权限或者角色；
            // 用户的帐户被删除/暂停。
            // 用户由管理员注销；
            assert userDetail != null;
            try {
                JwtUtil.verify(token, userDetail.getPassword().hashCode());
                // 如果本次 token 接近过期了(2天), 返回一个新token
                if (JwtUtil.isAlmostExpire(token)){
                    token = JwtUtil.createToken(loginName, userDetail.getPassword().hashCode());
                    response.setHeader(Constant.Header_Authorization, token);
                    response.setHeader(Constant.Header_Access_Control_Expose_Headers, Constant.Header_Authorization);
                    log.info("续签返回的new-Authorization-token: {}", token);
                }
            } catch (Exception e) {
                response401(response, EnumMsgCode.USER_NOT_LOGGED_IN, e);
                log.error("当前用户token不合法");
                return false;
            }
            /**
             * 菜单权限控制
             *
             * */
            String occMenuCode = request.getHeader(Constant.AUTH_CODE);
            if (occMenuCode == null){
                response401(response, EnumMsgCode.PARAM_ERROR,null);
                return false;
            }
            // subject 的 menus
            if (userDetail.getMenus() == null){
                response401(response, EnumMsgCode.PERMISSION_NO_ACCESS,null);
                log.error("当前用户menuCode为空");
                return false;
            }
            List<Menu> menus = userDetail.getMenus();
            for (Menu menu : menus) {
                if (Objects.equals(menu.getCode(),occMenuCode))
                    return true;
                List<Menu> childMenus = menu.getChildMenus();
                if (childMenus != null) {
                    for (Menu childMenu : childMenus) {
                        if (Objects.equals(childMenu.getCode(),occMenuCode))
                            return true;
                    }
                }
            }
            // 没权限
            response401(response, EnumMsgCode.PERMISSION_NO_ACCESS, null);
            log.error("当前请求: {}  用户权限无法满足当前menuCode: {}", request.getRequestURI(), occMenuCode);
            log.error("当前用户loginName为:{} \n当前用户的角色为:{} \n当前用户的数据权限类型为:{} \n当前用户可访问的Menu为:{}",userDetail.getLoginName(),userDetail.getRoleName(),userDetail.getDataRights(),userDetail.getMenus());
            return false;
        }
        // 不是控制器
        return true;
    }

    /**
     * 无需转发，直接返回Response信息
     */
    private void response401(HttpServletResponse response, EnumMsgCode msgCode, Object data) {

        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try (PrintWriter out = response.getWriter()) {
            // JsonString 很细节
            String returnValue = JSON.toJSONString(RestResponseModel.FAIL(msgCode, data));
            out.append(returnValue);
        } catch (IOException e) {
            log.error("直接返回Response信息出现IOException异常:{}", e.getMessage());
            throw new YuantongException(EnumMsgCode.MSG_SYS_ERROR);
        }
    }


}




